package com.radar.io;

import cn.hutool.core.io.FileUtil;
import com.radar.algorithm.Cacl;
import com.radar.algorithm.DenseLucaskanade;
import com.radar.algorithm.MathCommon;
import com.radar.algorithm.SemiLagrangian;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.exception.RadarDecodeError;
import com.radar.visualize.RasterMap;
import org.bytedeco.javacpp.indexer.Indexer;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


public class Type106Radar extends BaseRadr {

    public double[][] data;

    @Override
    public void parse(String file) throws RadarDecodeError {
        parse(file, 0);
    }

    public void parse(String file, int step) throws RadarDecodeError {
        byte[] bytes = FileUtil.readBytes(file);
        if (bytes.length < 180) {
            throw new RadarDecodeError("bytes length < 180");
        }
        data = new double[840][1240];
        int index = 360;
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[0].length; j++) {
                data[data.length - 1 - i][j] = bytes[index];
                index++;
            }
        }
        double[][] rdata = new double[data.length * step][data[0].length * step];
        if (step > 1) {
            for (int i = 0; i < rdata.length; i++) {
                for (int j = 0; j < rdata[0].length; j++) {
                    rdata[i][j] = data[i / step][j / step];
                }
            }
            data = rdata;
        }
    }

    public void drawItem(String outDir) throws IOException {
        FileUtil.mkdir(outDir);
        RasterMap rasterMap = new RasterMap(null);
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[0].length; j++) {
                data[i][j] = data[i][j] <= 0 ? GisConstants.UNDEF : data[i][j];
            }
        }
        data = Cacl.nineMedianFilter(data, 4);
        BufferedImage bufferedImage = rasterMap.drawSingleRaster(data, ProductType.CR, new Color(0, 0, 0, 0));
        ImageIO.write(bufferedImage, "png", new File(outDir + File.separator + "CR.png"));
        bufferedImage = rasterMap.drawSingleRaster(data, ProductType.REFGRAY, new Color(0, 0, 0, 0));
        ImageIO.write(bufferedImage, "png", new File(outDir + File.separator + "CR_GRAY.png"));
    }

    public void forecastItem(String prePath, String outDir) throws IOException {
        drawItem(outDir);
        if (FileUtil.exist(prePath + File.separator + "CR_GRAY.png")) {
            // forecast
            Mat ff1 = opencv_imgcodecs.imread(prePath + File.separator + "CR_GRAY.png");
            Mat ff2 = opencv_imgcodecs.imread(outDir + File.separator + "CR_GRAY.png");
            Mat prvs = ff1.clone();
            Mat next = ff1.clone();
            opencv_imgproc.cvtColor(ff1, prvs, opencv_imgproc.COLOR_BGR2GRAY);
            opencv_imgproc.cvtColor(ff2, next, opencv_imgproc.COLOR_BGR2GRAY);
            int w = prvs.size().get(0);
            int h = prvs.size().get(1);
            double[][] pre = new double[h][w];
            double[][] nex = new double[h][w];
            Indexer indexer = prvs.createIndexer();
            Indexer indexer1 = next.createIndexer();
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    pre[i][j] = indexer.getDouble(i, j);
                    if (pre[i][j] == 255) {
                        pre[i][j] = 0;
                    }
                    nex[i][j] = indexer1.getDouble(i, j);
                    if (nex[i][j] == 255) {
                        nex[i][j] = 0;
                    }
                }
            }
            double[][][] cacl = DenseLucaskanade.cacl(new double[][][]{pre, nex});
            double[][] u = cacl[0];
            double[][] v = cacl[1];
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    u[i][j] = u[i][j] * 0.5;
                    v[i][j] = v[i][j] * 0.5;
                }
            }
            SemiLagrangian semiLagrangian = new SemiLagrangian();
            int t = 20;
            float[][][] extrapolate = semiLagrangian.extrapolate(MathCommon.doubleToFloat(data), MathCommon.doubleToFloat(u), MathCommon.doubleToFloat(v), t);
            for (int i = 0; i < t; i++) {
                BufferedImage bufferedImage;
                for (int j = 0; j < u.length; j++) {
                    for (int k = 0; k < u[0].length; k++) {
                        extrapolate[i][j][k] = extrapolate[i][j][k] == 0 || extrapolate[i][j][k] == Float.NaN ? GisConstants.UNDEF : extrapolate[i][j][k];
                    }
                }
                RasterMap rasterMap = new RasterMap(null);
                bufferedImage = rasterMap.drawSingleRaster(MathCommon.floatToDouble(extrapolate[i]), new Color(0, 0, 0, 0));
                ImageIO.write(bufferedImage, "png", new File(outDir + File.separator + "CR_" + (i + 1) + ".png"));
            }
        }
    }

    @Override
    public void dispose() {

    }

    public static void main(String[] args) throws RadarDecodeError, IOException {
        long time = System.currentTimeMillis();
        Type106Radar radar = new Type106Radar();
        radar.parse("/Users/xx/DeskTop/radar_cref_202106121524.000", 3);
        radar.forecastItem("/Users/xx/DeskTop/202106121518", "/Users/xx/DeskTop/202106121524");

        radar = new Type106Radar();
        radar.parse("/Users/xx/DeskTop/radar_cref_202106121530.000", 3);
        radar.forecastItem("/Users/xx/DeskTop/202106121524", "/Users/xx/DeskTop/202106121530");
        System.out.println(System.currentTimeMillis() - time);
    }

}
